import { ResourceService } from "./resouce-service.manager";
import { Request, OpenedResourceId, InputUpdateRequest, Command, Location } from '../protocol/token.service';
import { InputElement, RootElement, MainElement, withLog } from './schema-element';
import { handleBlurElement, handleDelete, handleNextLine, toCommandOrUndo } from './element-handler';
import { elementToCommandList, findNodeByLocation, locationEq, logsToCommandList } from './schema-util';
import { ElementLog, undo } from './element-log';

export interface RootElementFactory {
    (): RootElement;
}

/**
 * Schema资源服务
 */
export class MemoryResourceService implements ResourceService {
    // 资源Id
    private resourceId: string;
    // 根元素
    private rootElement: RootElement;
    // 当前focus的元素
    private focusElementLocation: Location | undefined;

    constructor(resourceId: string, rootElementFactory: RootElementFactory) {
        this.resourceId = resourceId;
        this.rootElement = rootElementFactory();
        this.focusElementLocation = undefined;
    }

    open(): OpenedResourceId {
        return {
            resourceId: this.resourceId,
            commandList: elementToCommandList(this.rootElement.getChildren())
        };
    }
    async close() {
        return true;
    }

    /**
     * 处理请求
     */
    handleRequest(req: Array<Request>): Array<Command> {
        const commands: Array<Command> = [];
        for (const request of req) {
            switch (request.type) {
                case 'inputComplete': {
                    const { location, inputValue } = request;
                    const node = findNodeByLocation(this.rootElement, location);
                    const parentElement = node.value.parentElement!;

                    let logs: ElementLog[] = [];
                    try {
                        withLog(parentElement, logs, () =>{
                            parentElement.updateElementValue(node, inputValue);
                        });
                    } catch (e) {
                        console.error(`update element value error: ${e}`);
                        undo(logs);
                    }

                    const c = logsToCommandList(logs);
                    if (c.length > 0) {
                        commands.push.apply(commands, c);
                    }
                    break;
                }
                case 'completeContext': {
                    // 获取parent element
                    let { location } = request;
                    if (location.length < 2) {
                        console.log(`parent element is root`);
                        break;
                    }
                    location = location.slice(0, -1); // 去掉最后一个元素
                    const node = findNodeByLocation(this.rootElement, location);
                    const parentElement = node.value.parentElement!;
                    let logs: ElementLog[] = [];
                    try {
                        withLog(parentElement, logs, () => {
                            parentElement.childComplete(node);
                        });
                    } catch (e) {
                        console.error(`update element value error: ${e}`);
                        undo(logs);
                    }

                    const c = logsToCommandList(logs);
                    if (c.length > 0) {
                        commands.push.apply(commands, c);
                    }
                    break;
                }
                case 'nextLine': {
                    const { location } = request;
                    const node = findNodeByLocation(this.rootElement, location);
                    const parentElement = node.value.parentElement!;
                    let logs: ElementLog[] = [];
                    try {
                        // TODO offset在哪？
                        withLog(parentElement, logs, () =>{
                            parentElement.insertNextLine(1, node);
                        });
                    } catch (e) {
                        console.error(`update element value error: ${e}`);
                        undo(logs);
                    }

                    const c = toCommandOrUndo(handleNextLine({ location, element: node.value as MainElement, node }));
                    if (c.length > 0) {
                        commands.push.apply(commands, c);
                    }
                    break;
                }
                case 'delete': {
                    const { location } = request;
                    const node = findNodeByLocation(this.rootElement, location);
                    const c = toCommandOrUndo(handleDelete({ location, element: node.value as InputElement, node }));
                    if (c.length > 0) {
                        commands.push.apply(commands, c);
                    }
                    break;
                }
                case 'focusElement': {
                    const prev = this.focusElementLocation;
                    this.focusElementLocation = request.location;
                    if (!prev) {
                        break;
                    }
                    if (request.location && locationEq(request.location, prev)) {
                        break;
                    }

                    const node = findNodeByLocation(this.rootElement, prev);
                    const c = toCommandOrUndo(handleBlurElement(node));
                    if (c.length > 0) {
                        commands.push.apply(commands, c);
                    }
                    break;
                }
            }
        }
        return commands;
    }

    /**
     * 获取候选令牌
     */
    async getCandidateTokens(req: InputUpdateRequest): Promise<Array<string>> {
        const location = req.location;
        const inputValue = req.inputValue.toLowerCase();
        const element = findNodeByLocation(this.rootElement, location).value;
        if (!element) {
            return [];
        }
        return [];
    }
}
